/*
 *	dfregex.c
 *	© 2009 DigiFi Limited
 *
 *	Entry point into the test harness for the libdigifi library
 *
 */
#include <stdlib.h>
#include <stdio.h>
#include <regex.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>


#include "../headers/dfregex.h"
#include "../headers/dfregexcache.h"


#define LAST_ERROR_LENGTH 50

// private functions
static regex_t* dfregex_getregex(char* regex_name, int group);
extern char* dfregex_getregexstring(char *regex_name, int group);


// storing the last error
static char *last_error;

static pthread_mutex_t rx_mutex = PTHREAD_MUTEX_INITIALIZER;

int dfregex_init(void) {	
	// setup the last error holder
	if ((last_error = malloc(LAST_ERROR_LENGTH *sizeof(char))) == NULL) {return -DFCLIENT_ENOMEM;}
	
	return 0;
}


dfrx_result* dfregex_match(char *string, char *regex_name, df_commandtype group) {
	regmatch_t match[2];
	dfrx_result *result;
	regex_t *rx;
	int err;
	
	pthread_mutex_lock(&rx_mutex);
	
	rx = dfregex_getregex(regex_name, group);
	
	if (rx==NULL) { dferrors_printdebug("a horrible thing has occured\n"); return NULL;}
	
	#ifdef DF_DEBUG
	dferrors_printdebug("trying match. nse: %u rx: %p \n s: %ssp: %p  n: %p\n",rx->re_nsub, (void*)rx, string, (void*)string, (void*)match);
	#endif
	
	err = regexec(rx, string, 1, match, 0);
	
	if (err != 0) {
		pthread_mutex_unlock(&rx_mutex);
		regerror(err, rx, last_error, LAST_ERROR_LENGTH);
		
		if (REG_NOMATCH) {
			#ifdef DF_DEBUG
			dferrors_printerror(err,"Failed to match regex");
			#endif
		}
		else
		{ dferrors_printerror(err, "matches:%s", last_error); }
		
		return NULL;
	}
	
	pthread_mutex_unlock(&rx_mutex);
	
	#ifdef DF_DEBUG
	dferrors_printdebug("matched.\n");
	#endif
	
	if ((result = malloc(sizeof(dfrx_result))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex result holder."); return NULL;}
	
	result->start = match[0].rm_so;
	result->length = match[0].rm_eo - match[0].rm_so;
	
	if ((result->value = malloc((result->length+1) * sizeof(char))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex result value holder."); return NULL;}
	strncpy(result->value, string+(result->start*sizeof(char)), (size_t)result->length);
	result->value[result->length] = '\0';
	
	// result->num_subexps = 0;
	// result->subexps = NULL;
		 
	return result;
}

dfrx_result* dfregex_matches(char *string, char *regex_name, df_commandtype group) {
	regmatch_t *match;
	dfrx_result *result;
	regex_t *rx;
	int err;
	unsigned int i;
	
	pthread_mutex_lock(&rx_mutex);
	
	rx = dfregex_getregex(regex_name, group);
	
	if (rx==NULL) { dferrors_printdebug("a horrible thing has occured\n");}
	
	#ifdef DF_DEBUG
	dferrors_printdebug("trying matches. nse: %u rx: %p\ns: %s\n",rx->re_nsub, (void*)rx, string);
	#endif
	
	// setup match to hold as many sub exps as there are
	if ((match = malloc(sizeof(regmatch_t) * (rx->re_nsub+1))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex matches holder."); return NULL;}
	
	err = regexec(rx, string, rx->re_nsub+1, match, 0);
	
	if (err != 0) {
		pthread_mutex_unlock(&rx_mutex);
		regerror(err, rx, last_error, LAST_ERROR_LENGTH);
		
		if (REG_NOMATCH) {
			#ifdef DF_DEBUG
			dferrors_printerror(err,"Failed to match regex");
			#endif
		}
		else
		{ dferrors_printerror(err, "matches:%s", last_error); }
		
		return NULL;
	}
	
	#ifdef DF_DEBUG
	dferrors_printdebug("matched.\n");
	#endif
	
	pthread_mutex_unlock(&rx_mutex);
	
	if ((result = malloc(sizeof(dfrx_result))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex result holder."); return NULL;}
	
	result->start = match[0].rm_so;
	result->length = match[0].rm_eo - match[0].rm_so;
	
	if ((result->value = malloc((result->length+1) * sizeof(char))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex result value holder."); return NULL;}
	strncpy(result->value, string+(result->start*sizeof(char)), (size_t)result->length);
	result->value[result->length] = '\0';
	
	result->num_subexps = rx->re_nsub+1;
	if ((result->subexps = malloc(sizeof(dfrx_subexpression) * (rx->re_nsub+1))) == NULL)
	 	{dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex result subexp holder."); return NULL;}
	
	
	for (i=0; i<(rx->re_nsub+1); i++) {
		result->subexps[i].start = match[i].rm_so;
		result->subexps[i].length = match[i].rm_eo - match[i].rm_so;
		
		if ((result->subexps[i].value = malloc((result->subexps[i].length+1) * sizeof(char))) == NULL) 
			{dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex result subexp value holder."); return NULL;}
		strncpy(result->subexps[i].value, string+(result->subexps[i].start*sizeof(char)), result->subexps[i].length);
		result->subexps[i].value[result->subexps[i].length] = '\0';
	}
	
	free(match);
	
	return result;
}



void dfregex_freeresult(dfrx_result* tofree) {
	unsigned int i;
	
	if (tofree == NULL) return;
	
	for (i=0; i<tofree->num_subexps; i++) {
		free(tofree->subexps[i].value);
	}
	
	free(tofree->value);
	free(tofree->subexps);
	free(tofree);
}




regex_t* dfregex_getregex(char* regex_name, int group) {
	regex_t **rx_compiled;
	int err;
	char *regex;
	
	if ((rx_compiled = malloc(sizeof(regex_t **))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the regex pointer."); return NULL;}

	#ifdef DF_DEBUG
	dferrors_printdebug("rxn: %s\n", regex_name);
	#endif

	// try to get the regex from the cache
	if (dfregexcache_get(regex_name, (void**)rx_compiled) < 0) {
		// not in the cache so add it
		if ((*rx_compiled = malloc(sizeof(regex_t))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to malloc the compiled regex."); return NULL;}
		
		regex = dfregex_getregexstring(regex_name, group);
		
		if (regex== NULL) { free(*rx_compiled); free(rx_compiled); dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to get the regex."); return NULL; }
		
		#ifdef DF_DEBUG
		dferrors_printdebug("rxp: %p   regex: %s\n",(void*)*rx_compiled, regex);
		#endif
		
		err = regcomp(*rx_compiled, regex, REG_EXTENDED);
		
		if (err!=0) { 
			regerror(err, *rx_compiled, last_error, LAST_ERROR_LENGTH); 
			dferrors_printerror(err, "%s",last_error);
			free(*rx_compiled); free(rx_compiled); return NULL; }
		
		dfregexcache_cache(regex_name, *rx_compiled);
		
	}
	
	return *rx_compiled;
}

char* dfregex_getlasterror(void) {
	return last_error;
}

void dfregex_dump(void) { dfregexcache_dump(); }

void dfregex_dumprx(dfrx_result *rx) {
	int i;
	
	dferrors_printdebug("RegEx dump\n-------------------\n");
	
	if (rx==NULL) {dferrors_printdebug("(null)"); return;}
	
	if (rx->num_subexps == 0) {dferrors_printdebug("(empty)"); return;}
	
	for (i=0; i<rx->num_subexps; i++) {
		dferrors_printdebug("[%d] %s\n", i, rx->subexps[i].value);
	}
}


void dfregex_free(void) {
	if (last_error != NULL) free(last_error);
	
}